﻿using System;
using WatiNCF.Extensions;
using WatiNCF.Steps;
using WatiN.Core;
using WatiN.Core.Interfaces;
using AttributeConstraint=WatiN.Core.Constraints.AttributeConstraint;

namespace WatiNCF
{
    public class WizardController<T> : IWizardController<T> where T : class
    {
        #region Events
        public event EventHandler<StepEventArgs> BeforeStepNext;
        public event EventHandler<StepEventArgs> AfterStepNext;
        public event EventHandler<StepEventArgs> BeforeStepPrevious;
        public event EventHandler<StepEventArgs> AfterStepPrevious;
        public event EventHandler<StepEventArgs> BeforeCancel;
        public event EventHandler<StepEventArgs> AfterCancel;
        #endregion

        public IE Browser
        {
            get { return Parent.Browser; }
        }
        public bool StopExecution { get; set; }
        public IPageController<T> Parent { get; set; }

        public WizardController(IPageController<T> parent)
        {
            Parent = parent;
        }

        #region Wizard Methods
        public T StepNext()
        {
            return StepNext(string.Empty);
        }

        public T StepNext(string stepName)
        {
            if (!StopExecution)
            {
                StopExecution = FireBeforeStepNext(stepName).StopExecution;
                if (!StopExecution)
                {
                    Button nextButton = Browser.Button(ByPartialId("StepNextButton"));
                    if (!nextButton.Exists)
                    {
                        nextButton = Browser.Button(ByPartialId("StartNextButton"));
                        if (!nextButton.Exists)
                        {
                            nextButton = Browser.Button(ByPartialId("FinishButton"));
                        }
                    }

                    if (nextButton.Exists)
                    {
                        nextButton.Click();
                        Browser.Div(ByPartialId("WizardUpdatePanel")).Table(ByPartialId("Wizard")).WaitUntilRemoved();
                    }
                    StopExecution = FireAfterStepNext(stepName).StopExecution;
                }
            }
            return Parent as T;
        }

        public T StepPrevious()
        {
            return this.StepPrevious(string.Empty);
        }

        public T StepPrevious(string stepName)
        {
            if (!StopExecution)
            {
                StopExecution = FireBeforeStepPrevious(stepName).StopExecution;
                if (!StopExecution)
                {
                    Button previousButton = Browser.Button(ByPartialId("StepPreviousButton"));
                    if (!previousButton.Exists)
                    {
                        previousButton = Browser.Button(ByPartialId("StartPreviousButton"));
                        if (!previousButton.Exists)
                        {
                            previousButton = Browser.Button(ByPartialId("FinishPreviousButton"));
                        }
                    }

                    if (previousButton.Exists)
                    {
                        previousButton.Click();
                    }

                    FireAfterStepPrevious(stepName);
                }
            }
            return this.Parent as T;
        }

        public T Cancel()
        {
            if (!StopExecution)
            {
                StopExecution = FireBeforeCancel().StopExecution;
                if (!StopExecution)
                {
                    Button cancelButton = Browser.Button(ByPartialId("StepCancelButton"));
                    if (!cancelButton.Exists)
                    {
                        cancelButton = Browser.Button(ByPartialId("StartCancelButton"));
                        if (!cancelButton.Exists)
                        {
                            cancelButton = Browser.Button(ByPartialId("FinishCancelButton"));
                        }
                    }

                    if (cancelButton.Exists)
                    {
                        cancelButton.Click();
                    }
                    FireAfterCancel();
                }
            }
            return this.Parent as T;
        } 
        #endregion

        #region Event Handlers
        private StepEventArgs FireBeforeStepNext(string stepName)
        {
            StepEventArgs args = new StepEventArgs(stepName);
            if (BeforeStepNext != null)
            {
                BeforeStepNext(this, args);
            }

            return args;
        }

        private StepEventArgs FireAfterStepNext(string stepName)
        {
            StepEventArgs args = new StepEventArgs(stepName);
            if (AfterStepNext != null)
            {
                AfterStepNext(this, args);
            }
            return args;

        }

        private StepEventArgs FireBeforeStepPrevious(string stepName)
        {
            StepEventArgs args = new StepEventArgs(stepName);
            if (BeforeStepPrevious != null)
            {
                BeforeStepPrevious(this, args);
            }

            return args;
        }

        private StepEventArgs FireAfterStepPrevious(string stepName)
        {
            StepEventArgs args = new StepEventArgs(stepName);
            if (AfterStepPrevious != null)
            {
                AfterStepPrevious(this, args);
            }
            return args;

        }

        private StepEventArgs FireBeforeCancel()
        {
            StepEventArgs args = new StepEventArgs();
            if (BeforeCancel != null)
            {
                BeforeCancel(this, args);
            }
            return args;
        }

        private StepEventArgs FireAfterCancel()
        {
            StepEventArgs args = new StepEventArgs();
            if (AfterCancel != null)
            {
                AfterCancel(this, args);
            }
            return args;
        }
        #endregion

        public T ResetExecutionFlag()
        {
            StopExecution = false;
            return this.Parent as T;
        }

        /// <summary>
        /// Gets an attribute constraint based using the partial element id supplied.
        /// </summary>
        /// <param name="partialElementId">The partial element id.</param>
        /// <returns></returns>
        private AttributeConstraint ByPartialId(string partialElementId)
        {
            return WatinFind.ByPartialId(partialElementId);
        }
    }
}
